Skip to main content

Wikilinks in Docusaurus

Docusaurus does not support Obsidian-style Wikilinks.

Docusaurus uses MDX Markdown. Sometimes, you may want to extend or tweak your Markdown syntax. For example: "adding support for wikilinks".

the answer is: create an MDX plugin!

MDX Plugins

MDX has a built-in plugin system that can be used to customize how the Markdown files will be parsed and transformed to JSX. There are three typical use-cases of MDX plugins:

  • Using existing remark plugins or rehype plugins;
  • Creating remark/rehype plugins to transform the elements generated by existing MDX syntax;
  • Creating remark/rehype plugins to introduce new syntaxes to MDX.

The MDX transpilation has two intermediate steps: Markdown AST (MDAST), and Hypertext AST (HAST), before arriving at the final JSX output. MDX plugins also come in two forms:

  1. Remark: processes the Markdown AST.
  2. Rehype: processes the Hypertext AST.

An existing plugin is landakram/remark-wiki-link. This plugin is able to translate Wikilinks in markdown link understood by docusaurus.

This plugin alone can't do the job. Unfortunately, remark-wiki-link doesn't have built-in functionality to resolve file locations in the file system.
This means that links may fail in case of markdown files located in subfolder.

However, we can create a custom solution that scans your documentation directory and builds a map of filenames to their locations.

import path from 'path';
import fs from 'fs'


// Function to recursively scan the docs directory and build a map of filenames to their paths
function buildFileMap(dir, fileMap = {}, baseDir = dir) {
const files = fs.readdirSync(dir);

for (const file of files) {
const filePath = path.join(dir, file);
const stat = fs.statSync(filePath);

if (stat.isDirectory()) {
buildFileMap(filePath, fileMap, baseDir);
} else if (path.extname(file) === '.md') {
const relativePath = path.relative(baseDir, filePath);
const fileName = path.basename(file, '.md');
fileMap[fileName.toLowerCase()] = relativePath;
}
}

return fileMap;
}

// Build the file map
const docsDir = path.join(__dirname, '../digital-garden');
const fileMap = buildFileMap(docsDir);

Then we can use this map in a custom resolver for remark-wiki-link.

import {wikiLinkPlugin} from 'remark-wiki-link'

// other conf...

docs: {
sidebarPath: './sidebars.js',
path: '../digital-garden',
routeBasePath: '/notes',
remarkPlugins: [
[wikiLinkPlugin,
{
permalinks: Object.keys(fileMap),
pageResolver: (name) => {
const lower = name.toLowerCase();
if (fileMap[lower]) {
// Remove the file extension and convert slashes to match Docusaurus URL format
return [fileMap[lower].replace(/\.md$/, '').replace(/\\/g, '/')];
}
return [lower];
},
hrefTemplate: (permalink) => {
return `/notes/${permalink}`;
},
aliasDivider: '|'
}
],
],
},

References